use bytes::Bytes;
use hashbrown::HashMap;
use xactor::{Addr, Message};

use crate::actors::{router::Router, TcpStreamActor};
use crate::prelude::*;
use crate::xactor_ext::RouterAddrExt;
use crate::Result;

type TcpMsgProcess = fn(Addr<TcpStreamActor>, &Addr<Router>, u32, Bytes) -> Result<()>;
type NatsMsgProcess = fn(reply: String, &Addr<Router>, Bytes) -> Result<()>;

pub struct MessageRegister {
    tcp_msgs: HashMap<u16, TcpMsgProcess>,
    nats_msgs: HashMap<u16, NatsMsgProcess>,
}

impl MessageRegister {
    pub fn new() -> Self {
        Self {
            tcp_msgs: HashMap::default(),
            nats_msgs: HashMap::default(),
        }
    }

    pub fn register<T>(&mut self)
    where
        T: Message<Result = ()> + HoneybeeMessage,
    {
        let r = self.tcp_msgs.insert(T::ID, |source, router, _, bytes| {
            let message: T = bincode::deserialize(bytes.as_ref())?;
            if let Err(err) = router.route(source, message) {
                error!("router message id:{:?} error:{}", T::ID, err);
            }
            Ok(())
        });
        assert!(r.is_none(), "Duplicated MessageId {}", T::ID);
    }

    pub fn register_rpc<T>(&mut self)
    where
        T: Message + HoneybeeMessage,
        <T as Message>::Result: RpcResult,
    {
        let r = self
            .tcp_msgs
            .insert(T::ID, |source, router, session, bytes| {
                let message: T = bincode::deserialize(bytes.as_ref())?;
                if let Err(err) = router.route_rpc(source, session, message) {
                    return Err(anyhow::anyhow!(
                        "router message id:{:?} error:{}",
                        T::ID,
                        err
                    ));
                }
                Ok(())
            });
        assert!(r.is_none(), "Duplicated MessageId {}", T::ID);
    }

    pub fn register_nats<T>(&mut self)
    where
        T: Message<Result = ()> + HoneybeeMessage,
    {
        let r = self.nats_msgs.insert(T::ID, |reply, router, bytes| {
            let message: T = bincode::deserialize(bytes.as_ref())?;
            if let Err(err) = router.route_nats(reply, message) {
                error!("router nats message id:{:?} error:{}", T::ID, err);
            }
            Ok(())
        });
        assert!(r.is_none(), "Duplicated MessageId {}", T::ID);
    }

    pub fn process(
        &self,
        source: Addr<TcpStreamActor>,
        router: &Addr<Router>,
        msg: HoneybeeRawMsg,
    ) -> Result<()> {
        let process = self.tcp_msgs.get(&msg.id).ok_or_else(|| {
            anyhow::anyhow!("msssage id:{} session:{} not registry", msg.id, msg.session)
        })?;
        process(source, router, msg.session, msg.bytes)
    }

    pub fn process_nats(
        &self,
        reply: String,
        router: &Addr<Router>,
        id: u16,
        msg: Bytes,
    ) -> Result<()> {
        let process = self
            .nats_msgs
            .get(&id)
            .ok_or_else(|| anyhow::anyhow!("nats msssage id:{} not registry", id))?;
        process(reply, router, msg)
    }
}
